package com.fs.luban.config;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import com.fs.luban.module.sys.service.SysResourceService;
import com.fs.luban.security.entrypoint.LubanAuthenticationEntryPoint;
import com.fs.luban.security.handler.LubanAccessDeniedHandler;
import com.fs.luban.security.handler.LubanLoginFailureHandler;
import com.fs.luban.security.handler.LubanLoginSucessHandler;
import com.fs.luban.security.handler.LubanLogoutSuccessHandler;
import com.fs.luban.security.interceptor.LubanAccessDecisionVoter;
import com.fs.luban.security.interceptor.LubanFilterInvocationSecurityMetadataSource;

/**
 * @title Security权限 配置
 * @author fengshi
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Value("${luban.auth.loginUrl}")
	private String LOGIN_URL;
	@Value("${luban.auth.loginOutUrl}")
	private String LOGIN_OUT_URL;
	@Value("${luban.auth.loginSuccessUrl}")
	private String LOGIN_SUCCESS_URL;
	@Value("${luban.swagger.enable:false}")
	private Boolean SWAGGER_ENABLE;

	@Autowired
	private UserDetailsService userDetailsService;
	@Autowired
	private SysResourceService sysResourceService;

	@Autowired
	private AuthenticationEntryPoint lubanAuthenticationEntryPoint;
	@Autowired
	private LubanLoginSucessHandler loginSucessHandler;
	@Autowired
	private LubanLoginFailureHandler loginFailureHandler;
	@Autowired
	private LubanLogoutSuccessHandler logoutSuccessHandler;
	@Autowired
	private LubanAccessDeniedHandler accessDeniedHandler;
	@Autowired
	private AccessDecisionManager accessDecisionManager;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// iframe
		http.headers().frameOptions().sameOrigin();
		// 兼容ajax处理
		http.exceptionHandling().authenticationEntryPoint(lubanAuthenticationEntryPoint)
				.accessDeniedHandler(accessDeniedHandler);
		// 配置
		if(SWAGGER_ENABLE){
			http.authorizeRequests().antMatchers("/swagger*/**","/v2/api-docs/**","/webjars/**").permitAll();
		}
		http.authorizeRequests()
				.antMatchers("/css/**", "/js/**", "/img/**", "/components/**").permitAll()
				.anyRequest().authenticated()
				.and().formLogin().loginPage(LOGIN_URL).loginProcessingUrl(LOGIN_URL) // 登录认证地址
				.successHandler(loginSucessHandler).defaultSuccessUrl(LOGIN_SUCCESS_URL, true)// 登陆成功后的地址
				.failureUrl(LOGIN_URL + "?error").failureHandler(loginFailureHandler).permitAll() // 登陆失败地址
				.and().logout().logoutUrl(LOGIN_OUT_URL).logoutSuccessHandler(logoutSuccessHandler).permitAll()
				.and().csrf().disable();

		// 注入自定义处理
		http.authorizeRequests().anyRequest().authenticated()
				.withObjectPostProcessor(objectPostProcessorFilterSecurityInterceptor());
	}

	public ObjectPostProcessor<FilterSecurityInterceptor> objectPostProcessorFilterSecurityInterceptor() {
		return new ObjectPostProcessor<FilterSecurityInterceptor>() {
			@Override
			public <O extends FilterSecurityInterceptor> O postProcess(O object) {
				LubanFilterInvocationSecurityMetadataSource securityMetadataSource = new LubanFilterInvocationSecurityMetadataSource(
						object.getSecurityMetadataSource(), sysResourceService);
				object.setSecurityMetadataSource(securityMetadataSource);
				object.setAccessDecisionManager(accessDecisionManager);
				return object;
			}
		};
	}

	@Bean
	public BCryptPasswordEncoder encoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	public LubanAccessDecisionVoter accessDecisionVoter() {
		return new LubanAccessDecisionVoter();
	}

	@Bean
	public LubanAuthenticationEntryPoint authenticationEntryPoint() {
		return new LubanAuthenticationEntryPoint();
	}

	@Bean
	public LubanLoginFailureHandler loginFailureHandler() {
		return new LubanLoginFailureHandler();
	}

	@Bean
	public LubanLoginSucessHandler loginSucessHandler() {
		return new LubanLoginSucessHandler();
	}

	@Bean
	public LubanLogoutSuccessHandler logoutSuccessHandler() {
		return new LubanLogoutSuccessHandler();
	}

	@Bean
	public LubanAccessDeniedHandler accessDeniedHandler() {
		return new LubanAccessDeniedHandler();
	}

	@Bean
	public AccessDecisionManager accessDecisionManager() {
		List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
		decisionVoters.add(new AuthenticatedVoter());
		decisionVoters.add(new RoleVoter());
		decisionVoters.add(new LubanAccessDecisionVoter());
		WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
		webExpressionVoter.setExpressionHandler(new DefaultWebSecurityExpressionHandler());
		decisionVoters.add(webExpressionVoter);
		AffirmativeBased accessDecisionManager = new AffirmativeBased(decisionVoters);
		return accessDecisionManager;
	}

}
